package cn.tedu.justone.pack01;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * ZZ：JustOne
 * VX：Andylau2020smile
 * DY：JustOne2018
 * ZT：《》
 */
public class Demo10_悲观读写锁ReentrantReadWriteLock {

    //悲观读写锁ReentrantReadWriteLock
    //悲观读写锁ReentrantReadWriteLock
    //悲观读写锁ReentrantReadWriteLock
    //Reentrant [riːˈɛntrənt] 可重入的

    //关于悲观读写锁
    // 1,业务开发中我们可能涉及到读写操作。如果我们使用Lock锁和synchronized关键字同步数据，
    //   对于【读读】而言两个线程也需要争抢锁，其实是没有意义的，造成性能损耗。
    //   我们想要的是：写的时候不能读，没有写的时候多条线程可以同时读，无需互斥，
    //   因此，我们需要读写锁ReentrantReadWriteLock。
    // 2,ReentrantReadWriteLock 提供了公平和非公平锁。关于公平锁和非公平锁，简单来说，
    //   公平锁就是竟争的线程按申请顺序来获得锁对象，
    //   非公平锁就是竟争对手随机获得锁对象。
    //   从性能上来看，非公平优于公平。
    //   ReentrantReadWriteLock默认是非公平锁。
    // 3,ReentrantReadWriteLock(以下简称rwLock)管理着 x.readLock()和x.writeLock()，
    //   只要x.writeLock()锁被获取了，x.readLock()锁前面的线程就必须全部等待。
    // 4,为什么要冠以"悲观"二字呢，因为只要x.writeLock()获取到了锁，无论它是否做了数据修改，
    //   ReentrantReadWriteLock都按"被修改过"来处理，"读"的线程都不请允许进入。
    // 5,缺点：当读锁过多、写锁少时，存在锁饥饿现象。
    //   也即是：读锁不会互斥，批量涌入，一直占着锁，导致写锁一直得不到机会。
    //   小白说话：过桥(读)与修桥(写)不能同时进行，大量的人一直不下桥，导致一直修不了桥。修桥饥饿。
    //

    //测试悲观读写锁
    // 1,创建5条线程执行readData()方法 和 writeData()方法。
    // 2,可以发现读的时候，几条线程是可以同时读的，没有互斥。
    // 3,可以发现写的时候，几条线程是有先后关系的，存在互斥。
    // 4,可以发现写的时候，一个读的线程都没有参与，存在互斥。
    // 5,当创建读写锁new ReentrantReadWriteLock(true)时如果不指定或指定为false，
    //   可以发现写的时候，"获得锁"的顺序与 "申请锁"的顺序不同，也即是非公平锁。
    //   如果指定为true的话，"获得锁"的顺序与 "申请锁"的顺序总是相同的，也即是公平锁。
    // 6,为什么rLock 与 wLock 能够互斥呢？因为它们都同由rwLock管理的。

    //读写锁
    static ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true);
    static Lock rLock = rwLock.readLock(); //读写
    static Lock wLock = rwLock.writeLock();//写锁

    public static void main(String[] args) {

        for (int i = 0; i < 5; i++) {
            new Thread("线程"+i){
                @Override
                public void run() {
                    writeData();
                    readData();
                }
            }.start();
        }
    }

    //读数据
    public static void readData(){
        try{
            rLock.lock();
            System.out.println(Thread.currentThread().getName() + "正在读数据," + System.currentTimeMillis());
            mySleep(10000);
        }finally {
            rLock.unlock();
            System.out.println(Thread.currentThread().getName() + "释放了rLock.lock()");
        }
    }

    //写数据
    public static void writeData(){
        try{
            System.out.println(Thread.currentThread().getName() + "在等待wLock.lock();");
            wLock.lock();
            System.out.println(Thread.currentThread().getName() + "正在写数据");
            mySleep(10000);
        }finally {
            wLock.unlock();
            //System.out.println(Thread.currentThread().getName() + "释放了wLock.lock()");
        }
    }

    //为了让代码更优雅，编写统一的休眠方法
    public static void mySleep(long m){
        try {
            Thread.sleep(m);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}










